// 12.3.1 函数模板实参
// 函数模板实参（Function Template Arguments）是指在调用函数模板时提供的具体类型或值，用于实例化函数模板。函数模板实参的处理有其特殊性，这使得函数模板比类模板更加灵活易用。

// 函数模板实参的推导
// 函数模板最显著的特点是能够从函数调用中自动推导模板参数：
template <typename T>
T max(T a, T b)
{
    return a < b ? b : a;
}

// 调用时自动推导T为int
int result = max(42, 77); // 两个值必须都是int
// 这种自动推导大大简化了函数模板的使用，无需像类模板那样显式指定模板参数。

// 显式指定模板实参
// 尽管函数模板可以自动推导参数，但也可以显式指定模板实参：
template <typename T>
T max(T a, T b)
{
    return a < b ? b : a;
}

// 显式指定T为double
double result = max<double>(42, 77.5);

// 显式指定在以下情况特别有用：
// 1.需要强制类型转换
// 2.返回类型与参数类型不同
// 3.编译器无法正确推导类型

// 部分指定模板实参
// 对于有多个模板参数的函数模板，可以只指定前面的参数，让后面的参数继续推导：
template <typename RetType, typename T1, typename T2>
RetType max(T1 a, T2 b)
{
    return a < b ? b : a;
}

// 只指定返回类型，参数类型继续推导
double result = max<double>(42, 77.5); // max<double, int, float>(42, 7.5);
// 这称为"部分指定模板实参"，遵循从左到右的顺序。

// 模板实参推导的规则
// 函数模板实参推导遵循一系列复杂的规则：
// 1.精确匹配：
template <typename T>
void f(T param);

f(42); // T推导为int

// 2.引用参数：
template <typename T>
void f(T &param);

int x = 42;
f(x); // T推导为int

// 3.const引用参数：
template <typename T>
void f(const T &param);

int x = 42;
f(x); // T推导为int

// 4.万能引用（C++11）：
template <typename T>
void f(T &&param);

int x = 42;
f(x);  // T推导为int&
f(42); // T推导为int

// 5.数组和函数参数：
template <typename T>
void f(T param);

int arr[10];
f(arr); // T推导为int*

void g();
f(g); // T推导为void(*)()

// 特殊情况和限制
// 函数模板实参推导有一些特殊情况和限制：
// 1.无法从返回类型推导：
template <typename T>
T create(); // 无法从调用create()推导T

// 2.多个参数的一致性：
template <typename T>
void f(T a, T b);

f(1, 2);   // 正确：T = int
f(1, 2.5); // 错误：T不能同时是int和double

// 函数模板的重载
// 函数模板可以重载，编译器会选择最匹配的版本：
// 通用版本
template <typename T>
T max(T a, T b)
{
    return a < b ? b : a;
}

// 特化版本
template <>
const char *max<const char *>(const char *a, const char *b)
{
    return strcmp(a, b) < 0 ? b : a;
}

// 非模板版本
int max(int a, int b)
{
    return a < b ? b : a;
}

// 调用时，编译器会按照以下顺序选择：
// 精确匹配的非模板函数
// 精确匹配的函数模板特化
// 需要类型转换的非模板函数
// 需要类型转换的函数模板
// 函数模板实参的灵活推导机制是C++模板系统的一个重要特性，它使得函数模板比类模板更加易用，同时保持了强大的类型安全性和通用性。